本系列文已改編成書「Arduino 自造趣:結合 JavaScript x Vue x Phaser 輕鬆打造個人遊戲機」,本書改用 Vue3 與 TypeScript 全面重構且加上更詳細的說明,
在此感謝 iT 邦幫忙、博碩文化與編輯小 p 的協助,歡迎大家前往購書,鱈魚在此感謝大家 (。・∀・)。
若想 DIY 卻不知道零件去哪裡買的讀者,可以參考此連結 。( •̀ ω •́ )✧
這個章節要開始建立「PWM 輸出視窗」。
名為「脈衝寬度調變」(Pulse-width modulation)。PWM 本質上算是一種數位訊號,但是透過調整頻寬的方式,可以產生類似「類比訊號」的效果,是一種非常方便的技術,被大量應用在調速、通訊等等場合。
更詳細的說明可以參考以下連結:
PWM 脈衝寬度調變
內容大多可復用先前數位、類比功能視窗,所以過程大同小異 ∠( ᐛ 」∠)_
將 window-example.vue
複製一份後改個名字,建立 window-analog-input.vue
。
src\components\window-pwm-output.vue
<template lang="pug">
base-window.window-pwm-output(
:pos='pos',
header-icon-color='light-green-4',
body-class='c-col p-20px pt-20px',
title='PWM 輸出功能'
)
</template>
<style lang="sass">
.window-pwm-output
width: 300px
height: 400px
</style>
<script>
import BaseWindow from '@/components/base-window.vue';
import mixinWindow from '@/mixins/mixin-window';
export default {
name: 'WindowPwmOutput',
components: {
'base-window': BaseWindow,
},
mixins: [mixinWindow],
props: {},
data() {
return {};
},
computed: {},
watch: {},
created() {
console.log(`[ ${this.$options.name} created ] id : `, this.id);
},
mounted() {},
methods: {},
};
</script>
接著回到 app.vue
,將右鍵選單內加入『新增「PWM 輸出視窗」』選項,並引入組件。
src\app.vue <template lang="pug">
.screen(@click='handleClick')
// ...
// 右鍵選單
q-menu(context-menu, content-class='border-radius-s')
q-list.min-w-260px
q-item(@click='addWindow("window-digital-io")', clickable, v-close-popup)
q-item-section
| 新增「數位 I/O 視窗」
q-item(@click='addWindow("window-analog-input")', clickable, v-close-popup)
q-item-section
| 新增「類比輸入視窗」
q-item(@click='addWindow("window-pwm-output")', clickable, v-close-popup)
q-item-section
| 新增「PWM 輸出視窗」
src\app.vue <script>
// ...
import WindowDigitalIo from '@/components/window-digital-io.vue';
import WindowAnalogInput from '@/components/window-analog-input.vue';
import WindowPwmOutput from '@/components/window-pwm-output.vue';
export default {
name: 'App',
components: {
'dialog-system-setting': DialogSystemSetting,
'window-digital-io': WindowDigitalIo,
'window-analog-input': WindowAnalogInput,
'window-pwm-output': WindowPwmOutput,
},
// ...
};
一樣加入 base-select-pin.vue
。
src\components\window-pwm-output.vue <script>
import mixinWindow from '@/mixins/mixin-window';
import BaseWindow from '@/components/base-window.vue';
import BaseSelectPin from '@/components/base-select-pin.vue';
export default {
name: 'WindowPwmOutput',
components: {
'base-window': BaseWindow,
'base-select-pin': BaseSelectPin,
},
mixins: [mixinWindow],
props: {},
data() {
return {};
},
computed: {},
watch: {},
created() {
console.log(`[ ${this.$options.name} created ] id : `, this.id);
},
mounted() {},
methods: {},
};
src\components\window-pwm-output.vue <template lang="pug">
base-window.window-pwm-output(
:pos='pos',
header-icon-color='light-green-4',
body-class='c-col p-20px pt-20px',
title='PWM 輸出功能'
)
base-select-pin(color='light-green-4')
接著便是提供腳位清單了,在 computed
增加 supportPins
,提供支援 PWM 功能的腳位。
src\components\window-pwm-output.vue <script>
import { mapState } from 'vuex';
// ...
import { PinMode } from '@/script/utils/firmata.utils';
const { PWM } = PinMode;
export default {
name: 'WindowPwmOutput',
// ...
computed: {
...mapState({
boardPins: (state) => state.board.info.pins,
}),
// 支援功能的腳位
supportPins() {
/** @type {PinInfo[]} */
const boardPins = this.boardPins;
return boardPins.filter((pin) =>
pin.capabilities.some((capability) => PWM === capability.mode)
);
},
},
// ...
};
src\components\window-pwm-output.vue <template lang="pug">
base-window.window-pwm-output(
:pos='pos',
header-icon-color='light-green-4',
body-class='c-col p-20px pt-20px',
title='PWM 輸出功能'
)
base-select-pin(:pins='supportPins', color='light-green-4')
可以看到只有 Pin 3、5、6、9、10、11,比對一下 Uno 腳位,會注意到這些腳位數字旁邊都有個「~」符號,這些符號便是 PWM 功能的意思。
接下來依樣是儲存建立腳位的部分。
existPins
變數,儲存目前已建立腳位base-select-pin.vue
之 selected
事件,接收被選擇的腳位。src\components\window-pwm-output.vue <script>
/**
* @typedef {import('@/types/type').PinInfo} PinInfo
*/
import { mapState } from 'vuex';
import mixinWindow from '@/mixins/mixin-window';
import BaseWindow from '@/components/base-window.vue';
import BaseSelectPin from '@/components/base-select-pin.vue';
import { PinMode } from '@/script/utils/firmata.utils';
const { PWM } = PinMode;
export default {
name: 'WindowPwmOutput',
// ...
data() {
return {
/** @type {PinInfo[]} */
existPins: [],
};
},
// ...
methods: {
/** 新增腳位
* @param {PinInfo} pin
*/
addPin(pin) {
if (!pin) return;
this.$store.commit('window/addOccupiedPin', {
id: this.id,
pin,
});
this.existPins.push(pin);
},
/** 移除腳位
* @param {PinInfo} pin
*/
deletePin(pin) {
if (!pin) return;
this.$store.commit('window/deleteOccupiedPin', {
id: this.id,
pin,
});
const index = this.existPins.findIndex(
(existPin) => existPin.number === pin.number
);
this.existPins.splice(index, 1);
},
/** 接收錯誤訊息 */
handleErr(msg) {
this.$q.notify({
type: 'negative',
message: msg,
});
},
},
};
src\components\window-pwm-output.vue <template lang="pug">
base-window.window-pwm-output(
:pos='pos',
header-icon-color='light-green-4',
body-class='c-col p-20px pt-20px',
title='PWM 輸出功能'
)
base-select-pin(
:pins='supportPins',
color='light-green-4',
@selected='addPin',
@err='handleErr'
)
聰明的讀者們一定注意到了「這裡的步驟和數位、類比視窗都一樣」,所以大家也可以將這些重複內容抽出、建立 mixin。( ‧ ∀ ‧)ノ╰(‧ ∀ ‧ )
試試有沒有正常。
輕鬆愉快的完成 PWM 視窗,再來一樣進入建立控制組件環節。
base-select-pin
組件,用於選擇腳位。以上程式碼已同步至 GitLab,大家可以前往下載: